#include "linear.h"

GF256MDS::GF256MDS() {
    init_gf_mul_table();
    M = make_mds();
}

void GF256MDS::mds_map(const Bytes16& input, Bytes16& output) const {
    for (int i = 0; i < 16; ++i) {
        uint8_t acc = 0;
        for (int j = 0; j < 16; ++j) {
            if (input[j])
                acc ^= gf_mul_table[M[i][j]][input[j]];
        }
        output[i] = acc;
    }
}

void GF256MDS::xor_arrays(Bytes16& a, Bytes16& b) {
    for (size_t i = 0; i < 16; ++i) {
        b[i] ^= a[i];
    }
}

uint8_t GF256MDS::gf_mul(uint8_t a, uint8_t b) const {
    uint8_t res = 0;
    while (b) {
        if (b & 1) res ^= a;
        bool hi = a & 0x80;
        a <<= 1;
        if (hi) a ^= 0x1B;
        b >>= 1;
    }
    return res;
}

void GF256MDS::init_gf_mul_table() {
    for (int a = 0; a < 256; ++a) {
        for (int b = 0; b < 256; ++b) {
            gf_mul_table[a][b] = gf_mul(static_cast<uint8_t>(a), static_cast<uint8_t>(b));
        }
    }
}

GF256MDS::MDS16x16 GF256MDS::make_mds() const {
    MDS16x16 M;
    uint8_t pow[16];
    pow[0] = 1;
    for (int i = 1; i < 16; ++i)
        pow[i] = gf_mul_table[pow[i - 1]][ALPHA];

    for (int i = 0; i < 16; ++i)
        for (int j = 0; j < 16; ++j)
            M[i][j] = gf_mul_table[pow[j]][i];
    return M;
}
